package com.wans.utils.string;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;

/**
 * 加密工具类 - 支持多种对称和非对称加密算法
 */
@SuppressWarnings("all")
public class CryptoUtils {

    private CryptoUtils() {
        // 工具类禁止实例化
    }

    /* ================== 对称加密算法 ================== */

    // AES加密算法（支持不同工作模式和填充模式）
    public static final String AES_ECB_PKCS5 = "AES/ECB/PKCS5Padding";
    public static final String AES_CBC_PKCS5 = "AES/CBC/PKCS5Padding";
    public static final String AES_GCM_NO_PADDING = "AES/GCM/NoPadding";
    public static final int GCM_TAG_LENGTH = 128; // GCM标签长度

    // DES加密算法（兼容遗留系统）
    public static final String DES_CBC_PKCS5 = "DES/CBC/PKCS5Padding";

    /* ================== 哈希算法 ================== */
    public static final String MD5 = "MD5";
    public static final String SHA_256 = "SHA-256";
    public static final String SHA_512 = "SHA-512";

    /* ================== 密钥管理 ================== */

    /**
     * 生成AES密钥
     *
     * @param keySize 密钥长度（128/192/256）
     * @return 16进制格式密钥字符串
     */
    public static String generateAESKey(int keySize) throws NoSuchAlgorithmException {
        if (keySize != 128 && keySize != 192 && keySize != 256) {
            throw new IllegalArgumentException("无效的AES密钥长度");
        }

        KeyGenerator keyGen = KeyGenerator.getInstance("AES");
        keyGen.init(keySize);
        SecretKey secretKey = keyGen.generateKey();
        return bytesToHex(secretKey.getEncoded());
    }

    /**
     * 生成安全的随机初始化向量(IV)
     *
     * @param size IV字节长度
     * @return 16进制格式IV
     */
    public static String generateIV(int size) {
        byte[] iv = new byte[size];
        new SecureRandom().nextBytes(iv);
        return bytesToHex(iv);
    }

    /* ================== AES加密方法 ================== */

    /**
     * AES加密(支持GCM/CBC/ECB模式)
     *
     * @param plaintext 明文
     * @param key       密钥(16进制字符串)
     * @param algorithm 算法模式(AES_ECB_PKCS5等)
     * @param iv        初始化向量(除ECB模式外必须提供)
     * @return Base64格式加密字符串
     */
    public static String aesEncrypt(String plaintext, String key, String algorithm, String iv)
            throws Exception {
        if (plaintext == null) throw new IllegalArgumentException("明文不能为空");
        if (key == null || key.trim().isEmpty()) throw new IllegalArgumentException("密钥不能为空");

        byte[] keyBytes = hexToBytes(key);
        byte[] ivBytes = iv != null ? hexToBytes(iv) : new byte[0];
        byte[] plaintextBytes = plaintext.getBytes(StandardCharsets.UTF_8);

        SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
        Cipher cipher = Cipher.getInstance(algorithm);

        switch (algorithm) {
            case AES_ECB_PKCS5:
                cipher.init(Cipher.ENCRYPT_MODE, secretKey);
                break;
            case AES_CBC_PKCS5:
                cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(ivBytes));
                break;
            case AES_GCM_NO_PADDING:
                // 确保IV是12字节 (AES GCM最佳实践)
                if (ivBytes.length != 12) {
                    throw new IllegalArgumentException("GCM模式IV必须为12字节");
                }
                cipher.init(Cipher.ENCRYPT_MODE, secretKey,
                        new GCMParameterSpec(GCM_TAG_LENGTH, ivBytes));
                break;
            default:
                throw new IllegalArgumentException("不支持的模式: " + algorithm);
        }

        byte[] cipherText = cipher.doFinal(plaintextBytes);
        return Base64.getEncoder().encodeToString(cipherText);
    }

    /**
     * AES解密
     *
     * @param cipherText Base64格式加密文本
     * @param key        密钥(16进制字符串)
     * @param algorithm  算法模式
     * @param iv         初始化向量
     * @return 原始明文
     */
    public static String aesDecrypt(String cipherText, String key, String algorithm, String iv)
            throws Exception {
        if (cipherText == null || cipherText.trim().isEmpty()) {
            throw new IllegalArgumentException("密文不能为空");
        }

        byte[] keyBytes = hexToBytes(key);
        byte[] ivBytes = iv != null ? hexToBytes(iv) : new byte[0];
        byte[] cipherBytes = Base64.getDecoder().decode(cipherText);

        SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
        Cipher cipher = Cipher.getInstance(algorithm);

        switch (algorithm) {
            case AES_ECB_PKCS5:
                cipher.init(Cipher.DECRYPT_MODE, secretKey);
                break;
            case AES_CBC_PKCS5:
                cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(ivBytes));
                break;
            case AES_GCM_NO_PADDING:
                cipher.init(Cipher.DECRYPT_MODE, secretKey,
                        new GCMParameterSpec(GCM_TAG_LENGTH, ivBytes));
                break;
            default:
                throw new IllegalArgumentException("不支持的模式: " + algorithm);
        }

        byte[] plaintextBytes = cipher.doFinal(cipherBytes);
        return new String(plaintextBytes, StandardCharsets.UTF_8);
    }

    /* ================== 哈希函数 ================== */

    /**
     * MD5消息摘要
     *
     * @param input 输入文本
     * @return MD5哈希值(16进制)
     */
    public static String md5(String input) {
        return hash(MD5, input, "");
    }

    /**
     * MD5消息摘要
     *
     * @param input 输入文本
     * @param salt  盐值
     * @return MD5哈希值(16进制)
     */
    public static String md5(String input, String salt) {
        return hash(MD5, input, salt);
    }

    /**
     * SHA-256消息摘要
     *
     * @param input 输入文本
     * @param salt  盐值(可选)
     * @return SHA-256哈希值(16进制)
     */
    public static String sha256(String input, String salt) {
        return hash(SHA_256, input, salt);
    }

    /**
     * SHA-512消息摘要
     *
     * @param input 输入文本
     * @param salt  盐值(可选)
     * @return SHA-512哈希值(16进制)
     */
    public static String sha512(String input, String salt) {
        return hash(SHA_512, input, salt);
    }

    /* ================== 字节编解码 ================== */

    /**
     * 字节数组转16进制字符串
     */
    public static String bytesToHex(byte[] bytes) {
        if (bytes == null) return null;
        StringBuilder hexBuilder = new StringBuilder(2 * bytes.length);
        for (byte b : bytes) {
            hexBuilder.append(String.format("%02x", b));
        }
        return hexBuilder.toString();
    }

    /**
     * 16进制字符串转字节数组
     */
    public static byte[] hexToBytes(String hex) {
        if (hex == null) return null;
        int len = hex.length();
        if (len % 2 != 0) {
            throw new IllegalArgumentException("无效的16进制字符串");
        }
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
                    + Character.digit(hex.charAt(i + 1), 16));
        }
        return data;
    }
    /* ================== 哈希加密方法 ================== */

    /**
     * 哈希加密
     *
     * @param input 加密的文本
     * @param salt  盐1
     * @param salt2 盐2
     * @return 加密后的文本
     */
    public static String hashMD5(String password, String salt, String salt2) {
        return md5(md5(password, salt), salt2);
    }



    /* ================== 内部辅助方法 ================== */

    private static String hash(String algorithm, String input, String salt) {
        if (input == null) return null;
        try {
            java.security.MessageDigest md = java.security.MessageDigest.getInstance(algorithm);
            if (salt != null && !salt.isEmpty()) {
                md.update(salt.getBytes(StandardCharsets.UTF_8));
            }
            byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(digest);
        } catch (NoSuchAlgorithmException e) {
            throw new SecurityException("不支持的哈希算法: " + algorithm, e);
        }
    }

    /**
     * 安全对比两个哈希值(防止时序攻击)
     */
    public static boolean secureCompare(String hash1, String hash2) {
        if (hash1 == null || hash2 == null) return false;
        if (hash1.length() != hash2.length()) return false;

        int result = 0;
        for (int i = 0; i < hash1.length(); i++) {
            result |= hash1.charAt(i) ^ hash2.charAt(i);
        }
        return result == 0;
    }

    /* ================== 使用示例 ================== */
    public static void main(String[] args) throws Exception {
/*
        // 1. AES加密示例
        String key = generateAESKey(256);
        String iv = generateIV(12); // GCM需要12字节IV

        System.out.println("AES Key: " + key);
        System.out.println("IV: " + iv);

        String plaintext = "这是机密数据：账号123，密码456";
        System.out.println("原始文本: " + plaintext);

        // GCM模式加密（最安全模式）
        String encrypted = aesEncrypt(plaintext, key, AES_GCM_NO_PADDING, iv);
        System.out.println("AES-GCM加密结果: " + encrypted);

        // 解密
        String decrypted = aesDecrypt(encrypted, key, AES_GCM_NO_PADDING, iv);
        System.out.println("解密结果: " + decrypted);
*/

        // 2. 哈希示例
        String userPassword = "root";
        System.out.println(hashMD5(userPassword, "root", "root")); //10984c41f990c7c409ecc0da621b874f
/*        String password = "my-password123";
        String salt = "f1nd2c3#";
        String hashed = sha512(password, salt);
        System.out.println("SHA-512哈希: " + hashed);
        System.out.println("验证正确密码: " + secureCompare(hashed, sha512(password, salt)));
        System.out.println("验证错误密码: " + secureCompare(hashed, sha512("wrong-pass", salt)));

        */
    }
}